package main

import(
	"fmt"
	"time"
	//"os"
	"net/http"
	"strconv"
	"sync"
	"encoding/json"
	"github.com/samuel/go-zookeeper/zk"
	"math/rand"
	"io/ioutil"
)

type Server struct{
	Ipaddr string `json:ipaddr`
	Port int `json:port`
	Name string `json:name`
	Instance string `json:instance`
}

var ServerAMap []Server
var ServerBMap []Server

var mutexA sync.Mutex
var mutexB sync.Mutex

var wgA sync.WaitGroup
var wgB sync.WaitGroup

var isAOK chan bool
var isBOK chan bool

func main(){
	ServerAMap = []Server{}
	ServerBMap = []Server{}

	isAOK = make(chan bool);
	isBOK = make(chan bool);

	hosts := []string{"127.0.0.1:2181"}; 
	conn, _, err := zk.Connect(hosts, time.Second*5);
	if(err != nil){
		fmt.Println("zk connect failed, caused by " + err.Error());
		return
	}
	defer conn.Close();
	
	go watchServerA(conn);
	go watchServerB(conn);
	go doWithA()
	go doWithB()
	for{time.Sleep(time.Millisecond * 10)}
}

func watchServerA(conn *zk.Conn){
	exist, _, err := conn.Exists("/A") 
	if err != nil { 
		fmt.Println("zk Exists failed, caused by " + err.Error());
		return 
	} 
	if(!exist){
		var acls = zk.WorldACL(zk.PermAll);
		_, err := conn.Create("/A", []byte{}, 0, acls);
		if(err != nil){
			fmt.Println("zk create failed, caused by " + err.Error());
			return
		}
		fmt.Println("/A is create")
	}


	for{			
		children, _, A_instance, err := conn.ChildrenW("/A") 
		if err != nil { 
			fmt.Println(err) 
			continue 
		} 
		fmt.Printf("A instance[%d]\n", len(children)) 
	
		oldSize := len(ServerAMap)
		ServerAMap = []Server{}
		for idx, ch := range children { 
			fmt.Printf("%d, %s\n", idx, ch)
					
			dataByte, _, err := conn.Get("/A/" + ch);
			if(err != nil){
				fmt.Println("zk get failed, caused by " + err.Error());
				continue
			}
			serverInfo := Server{}
			json.Unmarshal(dataByte, &serverInfo)
			mutexA.Lock()
			ServerAMap = append(ServerAMap, serverInfo)
			mutexA.Unlock()
		}
		newSize := len(ServerAMap)
		fmt.Println(oldSize, newSize, ServerAMap)
		if (oldSize == 0 && newSize != 0){
			isAOK <- true
		}

		select { 
			case ch_event := <-A_instance: { 
				fmt.Println("path:", ch_event.Path) 
				fmt.Println("type:", ch_event.Type.String()) 
				fmt.Println("state:", ch_event.State.String()) 
			} 
		}
	}
}


func watchServerB(conn *zk.Conn){
	exist, _, err := conn.Exists("/B") 
	if err != nil { 
		fmt.Println("zk Exists failed, caused by " + err.Error());
		return 
	} 
	if(!exist){
		var acls = zk.WorldACL(zk.PermAll);
		_, err := conn.Create("/B", []byte{}, 0, acls);
		if(err != nil){
			fmt.Println("zk create failed, caused by " + err.Error());
			return
		}
		fmt.Println("/B is create")
	}


	for{			
		children, _, B_instance, err := conn.ChildrenW("/B") 
		if err != nil { 
			fmt.Println(err) 
			continue 
		} 
		fmt.Printf("B instance[%d]\n", len(children)) 
		
		
		oldSize := len(ServerBMap)
		ServerBMap = []Server{}
		for idx, ch := range children { 
			fmt.Printf("%d, %s\n", idx, ch)
						
			dataByte, _, err := conn.Get("/B/" + ch);
			if(err != nil){
				fmt.Println("zk get failed, caused by " + err.Error());
				continue
			}
			serverInfo := Server{}
			json.Unmarshal(dataByte, &serverInfo)
			mutexB.Lock()
			ServerBMap = append(ServerBMap, serverInfo)
			mutexB.Unlock()
		}
		newSize := len(ServerBMap)
		if (oldSize == 0 && newSize != 0){
			isBOK <- true
		}

		select { 
			case ch_event := <-B_instance: { 
				fmt.Println("path:", ch_event.Path) 
				fmt.Println("type:", ch_event.Type.String()) 
				fmt.Println("state:", ch_event.State.String()) 
			} 
		}
	}
}

func doWithA(){
	for{
		mutexA.Lock()
		length := len(ServerAMap)
		mutexA.Unlock()
		if(length  == 0){
			select{
			case <-isAOK:
				//do nothing
			}
		}

		mutexA.Lock()
		length = len(ServerAMap)
		mutexA.Unlock()
		rand.Seed(time.Now().UnixNano())
		idx := rand.Intn(length)
		Server := ServerAMap[idx]

		url := "http://" + Server.Ipaddr + ":" + strconv.Itoa(Server.Port) +"/A/hello"
		resp, err := http.Get(url)
		if(err != nil){
			fmt.Println(err)
			continue
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if(err != nil){
			fmt.Println(err)
			continue
		}
		fmt.Println(string(body))
		time.Sleep(time.Millisecond * 100)
	}
}

func doWithB(){
	for{
		mutexB.Lock()
		length := len(ServerBMap)
		mutexB.Unlock()
		if(length  == 0){
			select{
			case <-isBOK:
				//do nothing
			}
		}

		mutexB.Lock()
		length = len(ServerBMap)
		mutexB.Unlock()
		rand.Seed(time.Now().UnixNano())
		idx := rand.Intn(length)
		Server := ServerBMap[idx]

		url := "http://" + Server.Ipaddr + ":" + strconv.Itoa(Server.Port) +"/B/hello"
		resp, err := http.Get(url)
		if(err != nil){
			fmt.Println(err)
			continue
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if(err != nil){
			fmt.Println(err)
			continue
		}
		fmt.Println(string(body))
		time.Sleep(time.Millisecond * 100)
	}
}